"use server";

import { prisma } from "@/lib/prisma";
import { type Prisma } from "@prisma/client";
import { nextPage } from "@/utils";
import {
  type CreateBlogDTO,
  type GetBlogListDTO,
  type UpdateBlogDTO,
  verifyCreateBlog,
  verifyGetBlogList,
  verifyUpdateBlog,
} from "../types";
import { isUndefined } from "lodash-es";
import { PUBLISH_KAY_MAP } from "@/types/enum";

export const getBlogListMapper = async (params: GetBlogListDTO) => {
  const result = await verifyGetBlogList.safeParseAsync(params);

  if (!result.success) {
    throw new Error(result.error.format()._errors.join(";") || "");
  }

  const searchParams: Prisma.BlogWhereInput = {
    OR: [
      ...(result.data.title?.trim()
        ? [{ title: { contains: result.data.title.trim() } }]
        : []),
      ...(result.data.tags?.length
        ? [{ tags: { some: { id: { in: result.data.tags } } } }]
        : []),
    ],
  };

  if (!isUndefined(result.data.published)) {
    searchParams.published = PUBLISH_KAY_MAP[result.data.published];
  }

  if (!searchParams.OR?.length) {
    delete searchParams.OR;
  }

  const total = await prisma.blog.count();
  const blogs = await prisma.blog.findMany({
    include: {
      tags: true,
    },
    take: result.data.pageSize,
    skip: nextPage(result.data.pageIndex, result.data.pageSize),
    where: searchParams,
  });

  return { blogs, total };
};

export const getPublishedBlogListMapper = async () => {
  const blogs = await prisma.blog.findMany({
    orderBy: {
      createdAt: "desc",
    },
    include: {
      tags: true,
    },
    where: {
      published: true,
    },
  });

  const count = await prisma.blog.count({
    where: {
      published: true,
    },
  });

  const total = count ?? 0;

  return {
    blogs,
    total,
  };
};

export const getBlogByIDMapper = async (id: string) => {
  const blog = await prisma.blog.findUnique({
    where: { id },
    include: {
      tags: true,
    },
  });

  return { blog };
};

export const deleteBlogByIDMapper = async (id: string) => {
  await prisma.blog.delete({
    where: {
      id,
    },
  });
};

export const createBlogMapper = async (params: CreateBlogDTO) => {
  const result = await verifyCreateBlog.safeParseAsync(params);

  if (!result.success) {
    const error = result.error.format()._errors.join(";");
    throw new Error(error);
  }

  const { title, description, content, published, cover, tags } = result.data;

  const blogs = await prisma.blog.findMany({
    where: {
      OR: [{ title }],
    },
  });

  if (blogs.length) {
    throw new Error("标题重复");
  }

  await prisma.blog.create({
    data: {
      title,
      description,
      content,
      published,
      cover,
      tags: tags
        ? {
            connect: tags.map((tagID) => ({ id: tagID })),
          }
        : undefined,
    },
  });
};

export const toggleBlogPublishedByIdMapper = async (id: string) => {
  const blog = await prisma.blog.findUnique({
    where: {
      id,
    },
  });

  if (!blog) {
    throw new Error("Blog不存在");
  }

  await prisma.blog.update({
    data: {
      published: !blog.published,
    },
    where: {
      id,
    },
  });
};

export const updateBlogByIdMapper = async (
  id: string,
  params: UpdateBlogDTO
) => {
  const result = await verifyUpdateBlog.safeParseAsync(params);

  if (!result.success) {
    const error = result.error.format()._errors.join(";");
    throw new Error(error);
  }

  const { title, description, cover, content, published, tags } = result.data;

  const blog = await prisma.blog.findUnique({
    where: { id },
    include: { tags: true },
  });

  if (!blog) {
    throw new Error("Blog不存在");
  }

  const blogTags = new Set(blog.tags.map((el) => el.id));
  const tagsToConnect = tags
    ?.filter((tagID) => !blogTags.has(tagID))
    .map((tagID) => ({ id: tagID }));
  const tagsToDisconnect = Array.from(blogTags)
    .filter((tagID) => !tags?.includes(tagID))
    .map((tagID) => ({ id: tagID }));

  await prisma.blog.update({
    where: { id },
    data: {
      title: title ?? blog.title,
      description: description ?? blog.description,
      cover: cover ?? blog.cover,
      content: content ?? blog.content,
      published: published ?? blog.published,
      tags: {
        connect: tagsToConnect?.length ? tagsToConnect : undefined,
        disconnect: tagsToDisconnect.length ? tagsToDisconnect : undefined,
      },
    },
  });
};
